package com.ds.esd.localproxy.handler;


import com.alibaba.fastjson.JSONObject;
import com.ds.common.util.IOUtility;
import com.ds.server.httpproxy.core.*;
import com.ds.server.httpproxy.handler.ResourceHandler;
import com.ds.server.httpproxy.handler.multipart.SimpleRequestContext;
import com.ds.template.JDSFreemarkerResult;
import com.ds.vfs.VFSConstants;
import freemarker.template.TemplateException;
import org.apache.commons.fileupload.*;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.portlet.PortletFileUpload;
import org.apache.http.Header;
import org.apache.http.client.fluent.Form;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.InputStreamBody;
import org.apache.http.util.CharsetUtils;

import java.io.*;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import java.util.regex.Pattern;


public class HttpProxyHandler extends AbstractHandler {

    private static final Logger log = Logger.getLogger(ResourceHandler.class.getName());
    public static final ConfigOption PEOXYSERVER_OPTION = new ConfigOption("proxyServerUrl", "http://192.168.5.47:5338", "Regular expression for matching URLs.");

    Pattern rule;
    private String proxyurl;

    public boolean initialize(String handlerName, Server server) {
        super.initialize(handlerName, server);
        rule = Pattern.compile(RULE_OPTION.getProperty(server, handlerName));
        this.proxyurl = PEOXYSERVER_OPTION.getProperty(server, handlerName);
        return true;
    }


    protected boolean handleBody(HttpRequest request, HttpResponse response) throws IOException {

        boolean ruleMatches = rule.matcher(request.getUrl()).matches();
        if (!ruleMatches) {
            return false;
        }
        String resource = request.getUrl();
        String mimeType = getMimeType(resource);
        if (mimeType != null) {
            response.setMimeType(mimeType);
        }
        String contentType = this.getContentType(request);
        if (request.getMethod().toUpperCase().equals("GET")) {
            return this.sendGet(request, response, proxyurl);
        } else if (contentType != null && contentType.indexOf("application/octet-stream") > -1) {
            return this.sendStreamProxy(request, response, proxyurl);
        } else if (contentType != null && contentType.indexOf("multipart/form-data") > -1) {
            return this.sendMultiparPostProxy(request, response, proxyurl);
        } else if (request.getMethod().toUpperCase().equals("POST")) {
            return this.sendPostProxy(request, response, proxyurl);
        }

        return true;
    }


    public boolean sendStreamProxy(HttpRequest request, HttpResponse response, String url) throws IOException {
        String resource = this.formartUrl(request, url);
        org.apache.http.client.fluent.Request remoteRequest = org.apache.http.client.fluent.Request.Post(resource);
        Map headers = request.getHeaders().toMap();
        Iterator<String> nameIt = headers.keySet().iterator();
        for (; nameIt.hasNext(); ) {
            String name = nameIt.next();
            if (!name.toLowerCase().equals("content-length")) {
                remoteRequest.addHeader(name, (String) headers.get(name));
            }
        }
        return executeResponse(response, remoteRequest);
    }

    public boolean sendFtl(HttpRequest request, HttpResponse response, String resource) throws IOException {
        String responseStr = "";
        try {
            JDSFreemarkerResult result = new JDSFreemarkerResult();
            StringWriter stringWriter = (StringWriter) result.doExecute(resource, null);
            responseStr = stringWriter.toString();
        } catch (TemplateException e) {
            e.printStackTrace();
        }
        response.setMimeType("text/html");
        response.sendResponse(getInputStream(responseStr, "utf-8"),-1);
        return true;
        // return executeResponse(response, remoteRequest);
    }


    public boolean sendGet(HttpRequest request, HttpResponse response, String url) throws IOException {
        String resource = this.formartUrl(request, url);
        org.apache.http.client.fluent.Request remoteRequest = org.apache.http.client.fluent.Request.Get(resource);
        Map headers = request.getHeaders().toMap();
        Iterator<String> nameIt = headers.keySet().iterator();
        for (; nameIt.hasNext(); ) {
            String name = nameIt.next();
            remoteRequest.addHeader(name, (String) headers.get(name));
        }
        return executeResponse(response, remoteRequest);
    }

    public boolean sendPostProxy(HttpRequest request, HttpResponse response, String url) throws IOException {
        String resource = this.formartUrl(request, url);
        org.apache.http.client.fluent.Request remoteRequest = org.apache.http.client.fluent.Request.Post(resource);
        Map headers = request.getHeaders().toMap();
        Iterator<String> nameIt = headers.keySet().iterator();
        for (; nameIt.hasNext(); ) {
            String name = nameIt.next();
            if (!name.toLowerCase().equals("content-length")) {
                remoteRequest.addHeader(name, (String) headers.get(name));
            }
        }

        if (this.getContentType(request).indexOf("application/json") > -1) {
            String userData = JSONObject.toJSON(IOUtility.toString(request.getPostData())).toString();
            remoteRequest.bodyString(userData, ContentType.APPLICATION_JSON);
        } else {
            Map<String, String> paramsMap = request.getParameterMap();
            Form form = Form.form();
            Iterator<String> paramsIt = paramsMap.keySet().iterator();
            for (; paramsIt.hasNext(); ) {
                String name = paramsIt.next();
                form.add(name, paramsMap.get(name));
            }
            remoteRequest.bodyForm(form.build(), Charset.forName(VFSConstants.Default_Encoding));
        }

        return executeResponse(response, remoteRequest);
    }


    public boolean sendMultiparPostProxy(HttpRequest request, HttpResponse response, String url) throws IOException {
        String resource = this.formartUrl(request, url);

        org.apache.http.client.fluent.Request remoteRequest = org.apache.http.client.fluent.Request.Post(resource);

        Map headers = request.getHeaders().toMap();
        Iterator<String> nameIt = headers.keySet().iterator();
        for (; nameIt.hasNext(); ) {
            String name = nameIt.next();
            if (name.toLowerCase().equals("origin") || name.toLowerCase().equals(RefererHeard) || name.toLowerCase().equals("cookie")) {
                remoteRequest.addHeader(name, (String) headers.get(name));
            }
        }

        MultipartEntityBuilder reqEntitybuild = MultipartEntityBuilder.create().setMode(HttpMultipartMode.BROWSER_COMPATIBLE).setCharset(CharsetUtils.get(VFSConstants.CONFIG_CTVFS_KEY));
        // 获取服务器响应的IO流
        RequestContext requestContext = new SimpleRequestContext(StandardCharsets.UTF_8, this.getContentType(request), new ByteArrayInputStream(request.getPostData()));
        // 解析器创建
        FileUploadBase fileUploadBase = new PortletFileUpload();
        FileItemFactory fileItemFactory = new DiskFileItemFactory();
        fileUploadBase.setFileItemFactory(fileItemFactory);
        fileUploadBase.setHeaderEncoding(VFSConstants.CONFIG_CTVFS_KEY);
        // 解析出所有的部件
        List<FileItem> fileItems = null;
        try {
            fileItems = fileUploadBase.parseRequest(requestContext);
        } catch (FileUploadException e) {
            e.printStackTrace();
        }

        for (FileItem f : fileItems) {
            if (!f.isFormField()) {
                String fileName = f.getName();
                InputStreamBody fileStream = new InputStreamBody(f.getInputStream(), fileName);
                reqEntitybuild.addPart(f.getFieldName(), fileStream);
            } else {

                if (f != null && f.get() != null && f.get().length > 0) {
                    reqEntitybuild.addTextBody(f.getFieldName(), f.getString(VFSConstants.CONFIG_CTVFS_KEY));
                } else {
                    reqEntitybuild.addTextBody(f.getFieldName(), "");
                }

            }
        }
        remoteRequest = remoteRequest.body(reqEntitybuild.build());
        return executeResponse(response, remoteRequest);


    }


    public void copyStreamToFile(String url, File file) throws IOException {
        URL source = new URL(url);
        if (file.getParentFile() != null && !file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }
        if (file.exists() && !file.canWrite()) {
            final String message = "Unable to open file " + file + " for writing.";
            throw new IOException(message);
        }
        final InputStream input = source.openStream();
        final FileOutputStream output = new FileOutputStream(file);
        IOUtility.copy(input, output);
        IOUtility.shutdownStream(input);
        IOUtility.shutdownStream(output);
    }

    protected String getMimeType(String filename) {
        int index = filename.lastIndexOf(".");
        String mimeType = null;
        if (index > 0) {
            mimeType = server.getProperty("mime" + filename.substring(index).toLowerCase());
        }
        return mimeType;
    }

    private boolean executeResponse(HttpResponse response, org.apache.http.client.fluent.Request remoteRequest) throws IOException {
        org.apache.http.client.fluent.Response remoteresponse = remoteRequest.execute();
        org.apache.http.HttpResponse httpResponse = remoteresponse.returnResponse();
        Header[] reqheards = httpResponse.getAllHeaders();
        response.addHeader("Transfer-Encoding", null);
        for (Header header : reqheards) {
            if (!header.getName().equals("Transfer-Encoding")) {
                response.addHeader(header.getName(), header.getValue());
            }

        }
        if (httpResponse.getEntity().getContentLength() > 0) {
            response.addHeader("Content-Length", httpResponse.getEntity().getContentLength() + "");
            response.addHeader("Transfer-Encoding", null);
        }
        InputStream stream = httpResponse.getEntity().getContent();
        response.sendResponse(stream, Integer.valueOf(Long.toString(httpResponse.getEntity().getContentLength())));
        return true;
    }

    public String getContentType(HttpRequest request) {

        String contentType = request.getRequestHeader("Content-Type");
        if (contentType == null) {
            contentType = request.getRequestHeader("Content-type");
        }
        return contentType;
    }

    public InputStream getInputStream(String content, String charSet) {
        if (content == null) {
            return null;
        }
        try {
            return new ByteArrayInputStream(content.getBytes(charSet));
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    protected String formartUrl(HttpRequest request, String url) {
        String resource = request.getUrl();
        String queryStr = request.getQuery();

        if (queryStr != null && !queryStr.equals("")) {
            resource = resource + "?" + queryStr;
        }

        if (resource.startsWith("http://")) {
            resource = resource.substring("http://".length());
        }
        resource = url + resource;
        return resource;
    }


}
